package net.alche.fa.core.doing;

import java.lang.Override;
import net.alche.fa.core.model.Logic;
import net.alche.fa.core.model.FaValue;
import net.alche.fa.core.CodeLevel;
import net.alche.fa.core.SageStone;
import net.alche.fa.core.FaException;
import net.alche.fa.core.doing.sub.FaObjectSubAbs;
import net.alche.fa.core.doing.sub.StaticObjUtil;
import java.lang.reflect.Constructor;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import net.alche.fa.core.model.VarItem;
import net.alche.al.util.AC;
import net.alche.fa.core.model.FaCoreConf;
import java.util.Map;

// fa: 静态类
// @FaAnnoDoing
@SuppressWarnings("all")
public class FaDo extends AbstractDoing {

  @Override
  public boolean matched(String what, Logic g) {
    return false;
  }

  @Override
  public FaValue doing(Logic g, CodeLevel lvl, SageStone st) throws FaException {
    Object cls = null; // getStaticClass(g.getData())
    FaValue fav = new FaValue(cls, FaValue._OBJECT);
    return fav;
  }

  public FaValue getStaticObj(String dollarName, CodeLevel lvl) throws FaException {
    FaValue fav = this.plat.getStaticFa().get(dollarName);
    if (fav != null) {
      return fav;
    }
    Object cls = StaticObjUtil.newSpecialObj(dollarName, null, null);
    if (cls == null) {
      String staticNamePath = this.plat.getObjPath().get(dollarName);
      if (staticNamePath == null) {
        throw new FaException(FaException.CLASS_NOT_FIND, dollarName + "未配置包路径");
      }
      cls = this.newInstance(dollarName, this.checkParams(dollarName, staticNamePath));
      if (cls instanceof FaObjectSubAbs subAbs) {
        subAbs.setPlat(plat);
      }
    }
    if (cls instanceof FaValue) {
      fav = (FaValue) cls;
      this.plat.getStaticFa().put(dollarName, fav);
      return fav;
    }
    // Object cls = getStaticClass(dollarName)
    fav = new FaValue(cls, FaValue._OBJECT);
    this.plat.getStaticFa().put(dollarName, fav);
    return fav;
  }

  public Object[] checkParams(String className, String fullPath) {
    Object[] params = null;
    switch (className) {
      case "File": {
        params = new Object[] { "" };
        break;
      }
    }
    return params;
  }

  //
  public Object newInstance(String className, Object[] params) throws FaException {
    Object obj = null;
    Class cls = getObjClass(className);
    Class[] classes = new Class[params == null ? 0 : params.length];
    int i = 0;
    if (params != null && classes.length > 0) {
      for (Object par : params) {
        if (par == null) {
          classes[i++] = Object.class;
        } else {
          classes[i++] = par.getClass();
        }
      }
    }
    Constructor c = null;
    obj = StaticObjUtil.newSpecialObj(className, params, classes);
    if (obj != null) {
      return obj;
    }
    try {
      c = cls.getConstructor(classes);
    } catch (Exception ex) {
    }
    // throw new FaException(FaException.CLASS_CAN_NOT_NEW, className)
    if (c == null) {
      c = FaDo.findStruct(cls, classes);
    }
    if (c == null) {
      throw new FaException(FaException.CLASS_CAN_NOT_NEW, className);
    }
    try {
      // Constructor c = cls.getConstructor(classes)
      obj = c.newInstance(params);
      // obj = cls.getDeclaredConstructor().newInstance()
      return obj;
    } catch (Exception ex) {
      throw new FaException(FaException.CLASS_CAN_NOT_NEW, className);
    }
  }

  public static Constructor findStruct(Class cls, Class[] classes) {
    Constructor[] strcts = cls.getConstructors();
    Constructor cur = null;
    int like = 0;
    for (Constructor method : strcts) {
      if (method.getParameterCount() == classes.length) {
        if (cur == null) {
          cur = method;
        }
        int likeValue = DotDo.compareParamsValue(method.getParameterTypes(), classes);
        if (likeValue > like) {
          cur = method;
          like = likeValue;
        }
      }
    }
    return cur;
  }

  public boolean isObject(String className) {
    return this.plat.getObjPath().get(className) != null;
  }

  Class getObjClassBase(String className) {
    if (className.equals("String"))
      return String.class;
    if (className.equals("Integer"))
      return Integer.class;
    if (className.equals("Date"))
      return Date.class;
    if (className.equals("Long"))
      return Long.class;
    if (className.equals("Double"))
      return Double.class;
    return null;
  }

  public Class getObjClass(String className) throws FaException {
    Class cls = this.plat.getObjClasses().get(className);
    if (cls == null) {
      cls = getObjClassBase(className);
    }
    if (cls == null) {
      String fullName = this.plat.getObjPath().get(className);
      if (fullName == null) {
        fullName = className;
      }
      try {
        cls = Class.forName(fullName);
      } catch (ClassNotFoundException e) {
        throw new FaException(FaException.CLASS_NOT_FIND, fullName);
      }
    }
    return cls;
  }

  public void initStaticPath() {
    this.plat.setObjPath(new HashMap<>());
    this.plat.setObjClasses(new HashMap<>());
    this.plat.setStaticFa(new HashMap<>());
    String json = null;
    List<VarItem> viList = null;
    json = AC.file.readClassRes("/net/alche/fa/core/doing/sub/object-list.json");
    viList = AC.json.getList(json, VarItem.class);
    if (viList != null) {
      for (VarItem vi : viList) {
        this.plat.getObjPath().put(vi.getValue(), vi.getType());
      }
    }
    if (this.plat.extendsObjectList != null) {
      for (VarItem vi : this.plat.extendsObjectList) {
        this.plat.getObjPath().put(vi.getValue(), vi.getType());
      }
    }
    this.initBaseClassInfo();
  }

  public void initFaConf() {
    String json = null;
    FaCoreConf coreConf = null;
    json = AC.file.readClassRes("/net/alche/fa/core/doing/sub/core.json");
    coreConf = AC.json.getObj(json, FaCoreConf.class);
    this.plat.setConf(coreConf);
  }

  // 继承类型Class
  public void initBaseClassInfo() {
    Map<String, Class> map = this.plat.getObjClasses();
    map.put("int", Integer.class);
    map.put("Integer", Integer.class);
    map.put("long", Long.class);
    map.put("Long", Long.class);
  }
}